/* Copyright 2010, Object Management Group, Inc.
 * Copyright 2010, PrismTech, Inc.
 * Copyright 2010, Real-Time Innovations, Inc.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.omg.dds.sub;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.omg.dds.core.DDSObject;
import org.omg.dds.core.DomainEntity;
import org.omg.dds.core.Duration;
import org.omg.dds.core.InstanceHandle;
import org.omg.dds.core.NotEnabledException;
import org.omg.dds.core.PreconditionNotMetException;
import org.omg.dds.core.modifiable.ModifiableInstanceHandle;
import org.omg.dds.core.policy.DurabilityQosPolicy;
import org.omg.dds.core.policy.HistoryQosPolicy;
import org.omg.dds.core.status.LivelinessChangedStatus;
import org.omg.dds.core.status.RequestedDeadlineMissedStatus;
import org.omg.dds.core.status.RequestedIncompatibleQosStatus;
import org.omg.dds.core.status.SampleLostStatus;
import org.omg.dds.core.status.SampleRejectedStatus;
import org.omg.dds.core.status.SubscriptionMatchedStatus;
import org.omg.dds.domain.DomainParticipant;
import org.omg.dds.topic.ContentFilteredTopic;
import org.omg.dds.topic.MultiTopic;
import org.omg.dds.topic.PublicationBuiltinTopicData;
import org.omg.dds.topic.Topic;
import org.omg.dds.topic.TopicDescription;


/**
 * A DataReader allows the application (1) to declare the data it wishes to
 * receive (i.e., make a subscription) and (2) to access the data received by
 * the attached {@link Subscriber}.
 * 
 * A DataReader refers to exactly one {@link TopicDescription} (either a
 * {@link Topic}, a {@link ContentFilteredTopic}, or a {@link MultiTopic})
 * that identifies the data to be read. The subscription has a unique
 * resulting type. The data reader may give access to several instances of
 * the resulting type, which can be distinguished from each other by their
 * keys.
 * 
 * All operations except for the inherited operations
 * {@link #setQos(org.omg.dds.core.EntityQos)}, {@link #getQos()},
 * {@link #setListener(java.util.EventListener)}, {@link #getListener()},
 * {@link #enable()}, {@link #getStatusCondition()}, and {@link #close()} may
 * fail with the exception {@link NotEnabledException}.
 * 
 * All sample-accessing operations, namely all variants of {@link #read()} or
 * {@link #take()}, may fail with the exception
 * {@link PreconditionNotMetException}.
 * 
 * <b>Access to the Data</b>
 * 
 * Data is made available to the application by the following operations on
 * DataReader objects: {@link #read()}, {@link #take()}, and the other methods
 * beginning with those prefixes.. The general semantics of the "read"
 * operations is that the application only gets access to the corresponding
 * data; the data remains the middleware's responsibility and can be read
 * again. The semantics of the "take" operations is that the application
 * takes full responsibility for the data; that data will no longer be
 * accessible to the DataReader. Consequently, it is possible for a
 * DataReader to access the same sample multiple times but only if all
 * previous accesses were read operations.
 * 
 * Each of these operations returns an ordered collection of {@link Sample}s
 * (data values and associated meta-information). Each data value represents
 * an atom of data information (i.e., a value for one instance). This
 * collection may contain samples related to the same or different instances
 * (identified by the key). Multiple samples can refer to the same instance
 * if the settings of the {@link HistoryQosPolicy} allow for it.
 * 
 * @param <TYPE>    The concrete type of the data to be read.
 */
public interface DataReader<TYPE>
extends DomainEntity<DataReader<TYPE>,
                     Subscriber,
                     DataReaderListener<TYPE>,
                     DataReaderQos> {
    /**
     * Cast this data reader to the given type, or throw an exception if
     * the cast fails.
     * 
     * @param <OTHER>   The type of the data subscribed to by this reader,
     *                  according to the caller.
     * @return          this data reader
     * @throws          ClassCastException if the cast fails
     */
    public <OTHER> DataReader<OTHER> cast();

    /**
     * This operation creates a ReadCondition. The returned ReadCondition
     * will be attached and belong to the DataReader.
     * 
     * @param   sampleStates    The returned condition will only trigger on
     *          samples with one of these sample states.
     * @param   viewStates      The returned condition will only trigger on
     *          samples with one of these view states.
     * @param   instanceStates  The returned condition will only trigger on
     *          samples with one of these instance states.
     */
    public ReadCondition<TYPE> createReadCondition(
            Subscriber.ReaderState states);

    /**
     * This operation creates a QueryCondition. The returned QueryCondition
     * will be attached and belong to the DataReader. It will trigger on any
     * sample state, view state, or instance state.
     * 
     * @param   queryExpression The returned condition will only trigger on
     *          samples that pass this content-based filter expression.
     * @param   queryParameters A set of parameter values for the
     *          queryExpression.
     *
     * @see     #createQueryCondition(Collection, Collection, Collection, String, List)
     */
    public QueryCondition<TYPE> createQueryCondition(
            String queryExpression,
            List<String> queryParameters);

    /**
     * This operation creates a QueryCondition. The returned QueryCondition
     * will be attached and belong to the DataReader.
     * 
     * @param   sampleStates    The returned condition will only trigger on
     *          samples with one of these sample states.
     * @param   viewStates      The returned condition will only trigger on
     *          samples with one of these view states.
     * @param   instanceStates  The returned condition will only trigger on
     *          samples with one of these instance states.
     * @param   queryExpression The returned condition will only trigger on
     *          samples that pass this content-based filter expression.
     * @param   queryParameters A set of parameter values for the
     *          queryExpression.
     *
     * @see     #createQueryCondition(String, List)
     */
    public QueryCondition<TYPE> createQueryCondition(
            Subscriber.ReaderState states,
            String queryExpression,
            List<String> queryParameters);

    /**
     * This operation closes all the entities that were created by means of
     * the "create" operations on the DataReader. That is, it closes all
     * contained ReadCondition and QueryCondition objects.
     * 
     * @throws  PreconditionNotMetException     if the any of the contained
     *          entities is in a state where it cannot be closed.
     */
    public void closeContainedEntities();

    /**
     * @return  the TopicDescription associated with the DataReader. This is
     *          the same TopicDescription that was used to create the
     *          DataReader.
     */
    public TopicDescription<TYPE> getTopicDescription();

    /**
     * This operation allows access to the SAMPLE_REJECTED communication
     * status.
     * 
     * @param   status  a container, into which this method places it result.
     * @return  the input status, as a convenience to facilitate chaining.
     * 
     * @see     org.omg.dds.core.status
     */
    public SampleRejectedStatus getSampleRejectedStatus(
            SampleRejectedStatus status);

    /**
     * This operation allows access to the LIVELINESS_CHANGED communication
     * status.
     * 
     * @param   status  a container, into which this method places it result.
     * @return  the input status, as a convenience to facilitate chaining.
     * 
     * @see     org.omg.dds.core.status
     */
    public LivelinessChangedStatus getLivelinessChangedStatus(
            LivelinessChangedStatus status);

    /**
     * This operation allows access to the REQUESTED_DEADLINE_MISSED
     * communication status.
     * 
     * @param   status  a container, into which this method places it result.
     * @return  the input status, as a convenience to facilitate chaining.
     * 
     * @see     org.omg.dds.core.status
     */
    public RequestedDeadlineMissedStatus getRequestedDeadlineMissedStatus(
            RequestedDeadlineMissedStatus status);

    /**
     * This operation allows access to the REQUESTED_INCOMPATIBLE_QOS
     * communication status.
     * 
     * @param   status  a container, into which this method places it result.
     * @return  the input status, as a convenience to facilitate chaining.
     * 
     * @see     org.omg.dds.core.status
     */
    public RequestedIncompatibleQosStatus getRequestedIncompatibleQosStatus(
            RequestedIncompatibleQosStatus status);

    /**
     * This operation allows access to the SUBSCRIPTION_MATCHED communication
     * status. 
     * 
     * @param   status  a container, into which this method places it result.
     * @return  the input status, as a convenience to facilitate chaining.
     * 
     * @see     org.omg.dds.core.status
     */
    public SubscriptionMatchedStatus getSubscriptionMatchedStatus(
            SubscriptionMatchedStatus status);

    /**
     * This operation allows access to the SAMPLE_LOST communication status.
     * 
     * @param   status  a container, into which this method places its
     *          result.
     * @return  the input status, as a convenience to facilitate chaining.
     * 
     * @see     org.omg.dds.core.status
     */
    public SampleLostStatus getSampleLostStatus(
            SampleLostStatus status);

    /**
     * This operation is intended only for DataReader entities for which
     * {@link DurabilityQosPolicy#getKind()} is not
     * {@link DurabilityQosPolicy.Kind#VOLATILE}.
     * 
     * As soon as an application enables a non-VOLATILE DataReader it will
     * start receiving both "historical" data, i.e., the data that was
     * written prior to the time the DataReader joined the domain, as well as
     * any new data written by the DataWriter entities. There are situations
     * where the application logic may require the application to wait until
     * all "historical" data is received. This is the purpose of this
     * operation.
     * 
     * The operation blocks the calling thread until either all "historical"
     * data is received, or else the duration specified by the max_Wait
     * parameter elapses, whichever happens first.
     * 
     * @throws  TimeoutException        if maxWait elapsed before all the
     *          data was received.
     * 
     * @see     #waitForHistoricalData(long, TimeUnit)
     */
    public void waitForHistoricalData(Duration maxWait)
    throws TimeoutException;

    /**
     * This operation is intended only for DataReader entities for which
     * {@link DurabilityQosPolicy#getKind()} is not
     * {@link DurabilityQosPolicy.Kind#VOLATILE}.
     * 
     * As soon as an application enables a non-VOLATILE DataReader it will
     * start receiving both "historical" data, i.e., the data that was
     * written prior to the time the DataReader joined the domain, as well as
     * any new data written by the DataWriter entities. There are situations
     * where the application logic may require the application to wait until
     * all "historical" data is received. This is the purpose of this
     * operation.
     * 
     * The operation blocks the calling thread until either all "historical"
     * data is received, or else the duration specified by the max_Wait
     * parameter elapses, whichever happens first.
     * 
     * @throws  TimeoutException        if maxWait elapsed before all the
     *          data was received.
     * 
     * @see     #waitForHistoricalData(Duration)
     */
    public void waitForHistoricalData(long maxWait, TimeUnit unit)
    throws TimeoutException;

    /**
     * This operation retrieves the list of publications currently
     * "associated" with the DataReader; that is, publications that have a
     * matching {@link Topic} and compatible QoS that the application has not
     * indicated should be "ignored" by means of
     * {@link DomainParticipant#ignorePublication(InstanceHandle)}.
     * 
     * The handles returned in the 'publicationHandles' list are the ones
     * that are used by the DDS implementation to locally identify the
     * corresponding matched DataWriter entities. These handles match the
     * ones that appear in {@link Sample#getInstanceHandle()} when reading
     * the "DCPSPublications" built-in topic.
     * 
     * The operation may fail if the infrastructure does not locally maintain
     * the connectivity information.
     * 
     * @param   publicationHandles      a container, into which this method
     *          will place its result.
     * 
     * @return  publicationHandles, as a convenience to facilitate chaining.
     * 
     * @see     #getMatchedPublicationData(PublicationBuiltinTopicData, InstanceHandle)
     */
    public Collection<InstanceHandle> getMatchedPublications(
            Collection<InstanceHandle> publicationHandles);

    /**
     * This operation retrieves information on a publication that is
     * currently "associated" with the DataReader; that is, a publication
     * with a matching {@link Topic} and compatible QoS that the application
     * has not indicated should be "ignored" by means of
     * {@link DomainParticipant#ignorePublication(InstanceHandle)}.
     * 
     * The operation {@link #getMatchedPublications(Collection)} can be used
     * to find the publications that are currently matched with the
     * DataReader.
     * 
     * @param   publicationData         a container, into which this method
     *          will place its result.
     * @param   publicationHandle       a handle to the publication, the
     *          data of which is to be retrieved.
     * 
     * @return  subscriptionData, as a convenience to facilitate chaining.
     * 
     * @throws  IllegalArgumentException        if the publicationHandle does
     *          not correspond to a publication currently associated with the
     *          DataReader.
     * @throws  UnsupportedOperationException   if the infrastructure does
     *          not hold the information necessary to fill in the
     *          publicationData.
     *
     * @see     #getMatchedPublications(Collection)
     */
    public PublicationBuiltinTopicData getMatchedPublicationData(
            PublicationBuiltinTopicData publicationData,
            InstanceHandle publicationHandle);


    // --- Type-specific interface: ------------------------------------------

    /**
     * This operation accesses a collection of samples from this DataReader.
     * It behaves exactly like {@link #read(Query)} except that the
     * collection of returned samples is not constrained by any Query.
     * 
     * @return  a non-null unmodifiable iterator over loaned samples.
     * 
     * @see     #read(Query)
     * @see     #read(List)
     * @see     #read(List, Query)
     * @see     #readNextSample(Sample)
     * @see     #take()
     */
    public Sample.Iterator<TYPE> read();

    /**
     * This operation accesses a collection of samples from this DataReader.
     * The returned samples will be limited by the given {@link Query}. The
     * setting of the {@link org.omg.dds.core.policy.PresentationQosPolicy}
     * may impose further limits on the returned samples.
     * 
     * <ol>
     *     <li>If
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy#getAccessScope()}
     *         is
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy.AccessScopeKind#INSTANCE},
     *         then samples belonging to the same data instance are consecutive.
     *         </li>
     *     <li>If
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy#getAccessScope()}
     *         is
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy.AccessScopeKind#TOPIC}
     *         and
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy#isOrderedAccess()}
     *         is set to false, then samples belonging to the same data
     *         instance are consecutive.</li>
     *     <li>If
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy#getAccessScope()}
     *         is
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy.AccessScopeKind#TOPIC}
     *         and
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy#isOrderedAccess()}
     *         is set to true, then samples belonging to the same instance
     *         may or may not be consecutive. This is because to preserve
     *         order it may be necessary to mix samples from different
     *         instances.</li>
     *     <li>If
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy#getAccessScope()}
     *         is
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy.AccessScopeKind#GROUP}
     *         and
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy#isOrderedAccess()}
     *         is set to false, then samples belonging to the same data
     *         instance are consecutive.</li>
     *     <li>If
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy#getAccessScope()}
     *         is
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy.AccessScopeKind#GROUP}
     *         and
     *         {@link org.omg.dds.core.policy.PresentationQosPolicy#isOrderedAccess()}
     *         is set to true, then the returned collection contains at most
     *         one sample. The difference in this case is due to the fact
     *         that it is required that the application is able to read
     *         samples belonging to different DataReader objects in a
     *         specific order.
     *         </li>
     * </ol>
     * 
     * In any case, the relative order between the samples of one instance is
     * consistent with the
     * {@link org.omg.dds.core.policy.DestinationOrderQosPolicy}:
     * 
     * <ul>
     *     <li>If
     *         {@link org.omg.dds.core.policy.DestinationOrderQosPolicy#getKind()}
     *         is
     *         {@link org.omg.dds.core.policy.DestinationOrderQosPolicy.Kind#BY_RECEPTION_TIMESTAMP},
     *         samples belonging to the same instances will appear in the
     *         relative order in which they were received (FIFO, earlier
     *         samples ahead of the later samples).</li>
     *     <li>If
     *         {@link org.omg.dds.core.policy.DestinationOrderQosPolicy#getKind()}
     *         is
     *         {@link org.omg.dds.core.policy.DestinationOrderQosPolicy.Kind#BY_SOURCE_TIMESTAMP},
     *         samples belonging to the same instances will appear in the
     *         relative order implied by the result of
     *         {@link Sample#getSourceTimestamp()} (FIFO, smaller values of
     *         the source time stamp ahead of the larger values).<li>
     * </ul>
     * 
     * In addition to the sample data, the read operation also provides
     * sample meta-information ("sample info"). See {@link Sample}.
     * 
     * The returned samples are "loaned" by the DataReader. The use of this
     * variant allows for zero-copy (assuming the implementation supports it)
     * access to the data and the application will need to "return the loan"
     * to the DataReader using the {@link Sample.Iterator#returnLoan()}
     * operation.
     * 
     * Some elements in the returned collection may not have valid data. If
     * the instance state in the Sample is
     * {@link InstanceState#NOT_ALIVE_DISPOSED} or
     * {@link InstanceState#NOT_ALIVE_NO_WRITERS}, then the last sample for
     * that instance in the collection, that is, the one with
     * {@link Sample#getSampleRank()} == 0, does not contain valid data.
     * Samples that contain no data do not count towards the limits imposed
     * by the {@link org.omg.dds.core.policy.ResourceLimitsQosPolicy}.
     * 
     * The act of reading a sample sets its sample state to
     * {@link SampleState#READ}. If the sample belongs to the most recent
     * generation of the instance, it will also set the view state of the
     * instance to {@link ViewState#NOT_NEW}. It will not affect the
     * instance state of the instance.
     * 
     * If the DataReader has no samples that meet the constraints, the
     * return value will be a non-null iterator that provides no samples.
     * 
     * @return  a non-null unmodifiable iterator over loaned samples.
     * 
     * @see     #read()
     * @see     #read(List)
     * @see     #read(List, Query)
     * @see     #readNextSample(Sample)
     * @see     #take(Query)
     * @see     #createQuery(InstanceHandle, int, boolean)
     */
    public Sample.Iterator<TYPE> read(Query<TYPE> query);

    /**
     * This operation accesses a collection of samples from this DataReader.
     * It behaves exactly like {@link #read()} except that the returned
     * samples are not "on loan" from the Service; they are deeply copied to
     * the application.
     * 
     * The read operation will copy the data and meta-information into the
     * elements already inside the given collection, overwriting any samples
     * that might already be present. The use of this variant forces a copy
     * but the application can control where the copy is placed and the
     * application will not need to "return the loan."
     * 
     * @return  <code>samples</code>, for convenience.
     * 
     * @see     #read()
     * @see     #read(Query)
     * @see     #read(List, Query)
     * @see     #readNextSample(Sample)
     * @see     #take(List)
     */
    public List<Sample<TYPE>> read(List<Sample<TYPE>> samples);

    /**
     * This operation accesses a collection of samples from this DataReader.
     * It behaves exactly like {@link #read(Query)} except that the returned
     * samples are not "on loan" from the Service; they are deeply copied to
     * the application.
     * 
     * The read operation will copy the data and meta-information into the
     * elements already inside the given collection, overwriting any samples
     * that might already be present. The use of this variant forces a copy
     * but the application can control where the copy is placed and the
     * application will not need to "return the loan."
     * 
     * @return  <code>samples</code>, for convenience.
     * 
     * @see     #read()
     * @see     #read(Query)
     * @see     #read(List)
     * @see     #readNextSample(Sample)
     * @see     #take(List, Query)
     * @see     #createQuery(InstanceHandle, int, boolean)
     */
    public List<Sample<TYPE>> read(
            List<Sample<TYPE>> samples,
            Query<TYPE> query);

    /**
     * This operation accesses a collection of samples from this DataReader.
     * It behaves exactly like {@link #take(Query)} except that the
     * collection of returned samples is not constrained by any Query.
     * 
     * @return  a non-null unmodifiable iterator over loaned samples.
     * 
     * @see     #take(Query)
     * @see     #take(List)
     * @see     #take(List, Query)
     * @see     #takeNextSample(Sample)
     * @see     #read()
     */
    public Sample.Iterator<TYPE> take();

    /**
     * This operation accesses a collection of samples from this DataReader.
     * The number of samples returned is controlled by the
     * {@link org.omg.dds.core.policy.PresentationQosPolicy} and other
     * factors using the same logic as for {@link #read(Query)}.
     * 
     * The act of taking a sample removes it from the DataReader so it cannot
     * be "read" or "taken" again. If the sample belongs to the most recent
     * generation of the instance, it will also set the view state of the
     * instance to {@link ViewState#NOT_NEW}. It will not affect the
     * instance state of the instance.
     * 
     * The behavior of the take operation follows the same rules than the
     * read operation regarding the preconditions and postconditions for the
     * arguments and return results. Similar to read, the take operation will
     * "loan" elements to the application; this loan must then be returned by
     * means of {@link Sample.Iterator#returnLoan()}. The only difference
     * with read is that, as stated, the sample returned by take will no
     * longer be accessible to successive calls to read or take.
     * 
     * If the DataReader has no samples that meet the constraints, the
     * return value will be a non-null iterator that provides no samples.
     * 
     * @return  a non-null unmodifiable iterator over loaned samples.
     * 
     * @see     #take()
     * @see     #take(List)
     * @see     #take(List, Query)
     * @see     #takeNextSample(Sample)
     * @see     #read(Query)
     * @see     #createQuery(InstanceHandle, int, boolean)
     */
    public Sample.Iterator<TYPE> take(Query<TYPE> query);

    /**
     * This operation accesses a collection of samples from this DataReader.
     * It behaves exactly like {@link #take()} except that the returned
     * samples are not "on loan" from the Service; they are deeply copied to
     * the application.
     * 
     * The take operation will copy the data and meta-information into the
     * elements already inside the given collection, overwriting any samples
     * that might already be present. The use of this variant forces a copy
     * but the application can control where the copy is placed and the
     * application will not need to "return the loan."
     * 
     * @return  <code>samples</code>, for convenience.
     * 
     * @see     #take()
     * @see     #take(Query)
     * @see     #take(List, Query)
     * @see     #takeNextSample(Sample)
     * @see     #read(List)
     */
    public List<Sample<TYPE>> take(List<Sample<TYPE>> samples);

    /**
     * This operation accesses a collection of samples from this DataReader.
     * It behaves exactly like {@link #take(Query)} except that the returned
     * samples are not "on loan" from the Service; they are deeply copied to
     * the application.
     * 
     * The take operation will copy the data and meta-information into the
     * elements already inside the given collection, overwriting any samples
     * that might already be present. The use of this variant forces a copy
     * but the application can control where the copy is placed and the
     * application will not need to "return the loan."
     * 
     * @return  <code>samples</code>, for convenience.
     * 
     * @see     #take()
     * @see     #take(Query)
     * @see     #take(List)
     * @see     #takeNextSample(Sample)
     * @see     #read(List, Query)
     * @see     #createQuery(InstanceHandle, int, boolean)
     */
    public List<Sample<TYPE>> take(
            List<Sample<TYPE>> samples,
            Query<TYPE> query);

    /**
     * This operation copies the next, non-previously accessed sample from
     * this DataReader. The implied order among the samples stored in the
     * DataReader is the same as for {@link #read(List, Query)}.
     * 
     * This operation is semantically equivalent to
     * {@link #read(List, Query)} where {@link Query#getMaxSamples()}
     * is 1, {@link Query#getReaderState()} followed by
     * {@link Subscriber.ReaderState#getSampleStates()} ==
     * {@link SampleState#NOT_READ}, {@link Query#getReaderState()} followed by
     * {@link Subscriber.ReaderState#getViewStates()} contains all view
     * states, and {@link Query#getReaderState()} followed by
     * {@link Subscriber.ReaderState#getInstanceStates()} contains all
     * instance states.
     * 
     * This operation provides a simplified API to "read" samples avoiding
     * the need for the application to manage iterators and specify queries.
     * 
     * If there is no unread data in the DataReader, the operation will
     * return false and the provided sample is not modified.
     * 
     * @return  true if data was read or false if no data was available.
     * 
     * @see     #read()
     * @see     #read(Query)
     * @see     #read(List)
     * @see     #read(List, Query)
     * @see     #takeNextSample(Sample)
     */
    public boolean readNextSample(Sample<TYPE> sample);

    /**
     * This operation copies the next, non-previously accessed sample from
     * this DataReader and "removes" it from the DataReader so it is no
     * longer accessible. This operation is analogous to 
     * {@link #readNextSample(Sample)} except for the fact that the sample is
     * "removed" from the DataReader.
     * 
     * This operation is semantically equivalent to
     * {@link #take(List, Query)} where {@link Query#getMaxSamples()}
     * is 1, {@link Query#getReaderState()} followed by
     * {@link Subscriber.ReaderState#getSampleStates()} ==
     * {@link SampleState#NOT_READ}, {@link Query#getReaderState()} followed by
     * {@link Subscriber.ReaderState#getViewStates()} contains all view
     * states, and {@link Query#getReaderState()} followed by
     * {@link Subscriber.ReaderState#getInstanceStates()} contains all
     * instance states.
     * 
     * This operation provides a simplified API to "take" samples avoiding
     * the need for the application to manage iterators and specify queries.
     * 
     * If there is no unread data in the DataReader, the operation will
     * return false and the provided sample is not modified.
     * 
     * @return  true if data was taken or false if no data was available.
     * 
     * @see     #take()
     * @see     #take(Query)
     * @see     #take(List)
     * @see     #take(List, Query)
     * @see     #readNextSample(Sample)
     */
    public boolean takeNextSample(Sample<TYPE> sample);

    /**
     * This operation can be used to retrieve the instance key that
     * corresponds to an instance handle. The operation will only fill the
     * fields that form the key inside the keyHolder instance.
     * 
     * @param   keyHolder       a container, into which this method shall
     *          place its result.
     * @param   handle          a handle indicating the instance whose value
     *          this method should get.
     *
     * @return  keyHolder, as a convenience to facilitate chaining.
     * 
     * @throws  IllegalArgumentException        if the {@link InstanceHandle}
     *          does not correspond to an existing data object known to the
     *          DataReader. If the implementation is not able to check
     *          invalid handles, then the result in this situation is
     *          unspecified.
     */
    public TYPE getKeyValue(
            TYPE keyHolder, 
            InstanceHandle handle);

    /**
     * This operation takes as a parameter an instance and returns a handle
     * that can be used in subsequent operations that accept an instance
     * handle as an argument. The instance parameter is only used for the
     * purpose of examining the fields that define the key.
     * 
     * This operation does not register the instance in question. If the
     * instance has not been previously registered, or if for any other
     * reason the Service is unable to provide an instance handle, the
     * Service will return a nil handle.
     * 
     * @param   handle  a container, into which this method shall place its
     *          result.
     * @param   keyHolder       a sample of the instance whose handle this
     *          method should look up.
     *
     * @return  handle, as a convenience to facilitate chaining.
     */
    public ModifiableInstanceHandle lookupInstance(
            ModifiableInstanceHandle handle,
            TYPE keyHolder);


    // --- Query: ------------------------------------------------------------

    /**
     * Create and return a new query to read or take samples of the
     * identified instance (or the "next" instance), regardless of
     * {@link Subscriber.ReaderState}.
     * 
     * If isReadOrTakeNext is true, the actual instance is not directly
     * specified. Rather the samples will all belong to the "next" instance
     * with instance handle "greater" (according to some service-defined
     * order) than the specified handle that has available samples.
     * 
     * The behavior of isReadOrTakeNext implies the existence of a total
     * order "greater-than" relationship between the instance handles. The
     * specifics of this relationship are not all important and are
     * implementation specific. The important thing is that, according to the
     * middleware, all instances are ordered relative to each other. This
     * ordering is between the instance handles: It should not depend on the
     * state of the instance (e.g., whether it has data or not) and must be
     * defined even for instance handles that do not correspond to instances
     * currently managed by the DataReader. For the purposes of the ordering
     * it should be "as if" each instance handle was represented as a unique
     * integer.
     * 
     * The behavior of {@link #read(Query)} or {@link #take(Query)} with
     * isReadOrTakeNext == true is "as if" isReadOrTakeNext == false and the
     * application passed the smallest instance handle among all the ones
     * that (a) are greater than the given handle and (b) have available
     * samples (i.e., samples that meet the constraints imposed by the
     * Query).
     * 
     * A nil handle is guaranteed to be "less than" any valid instance
     * handle. So the use of the parameter value handle ==
     * {@link InstanceHandle#nilHandle(org.omg.dds.core.Bootstrap)} will
     * return the samples for the instance which has the smallest
     * instance handle among all the instances that contain available
     * samples.
     * 
     * A value of isReadOrTakeNext == true is intended to be used in an
     * application-driven iteration where the application starts by passing
     * a nil handle, examines the samples returned, and then uses the
     * instance handle returned in the {@link Sample} as the value of the
     * handle argument to the next call to read or take. The iteration
     * continues until no data remains.
     * 
     * Note that it is possible to pass isReadOrTakeNext == true with a
     * non-nil handle that does not correspond to an instance currently
     * managed by the DataReader. This is because as stated earlier the
     * "greater-than" relationship is defined even for handles not managed by
     * the DataReader. One practical situation where this may occur is when
     * an application is iterating through all the instances, takes all the
     * samples of a NOT_ALIVE_NO_WRITERS instance, returns the loan (at which
     * point the instance information may be removed, and thus the handle
     * becomes invalid), and tries to read the next instance.
     * 
     * @param handle            The instance to read or take. If this handle
     *                          is null, or if it is nil and isReadOrTakeNext
     *                          is false, the read or take will return samples
     *                          of all available instances.
     * @param maxSamples        The maximum number of samples to read or take
     *                          at one time, or {@link Integer#MAX_VALUE} if
     *                          all available samples should be returned.
     * @param isReadOrTakeNext  If this flag is set to false,
     *                          the read or take will return samples of the
     *                          identified instance. If it is true, the read
     *                          or take will return samples of the "next"
     *                          instance in order. This behavior allows
     *                          applications to easily iterate over the
     *                          available data one instance at a time.
     * 
     * @return  a new Query.
     * 
     * @throws  IllegalArgumentException    if the handle is null and
     *                                      isReadOrTakeNext is true, or if
     *                                      the handle does not correspond to
     *                                      an existing data object known to
     *                                      this DataReader.
     * 
     * @see     #createQuery(InstanceHandle, int, boolean, ReadCondition)
     * @see     #createQuery(InstanceHandle, int, boolean, org.omg.dds.sub.Subscriber.ReaderState)
     * @see     #read(Query)
     * @see     #take(Query)
     */
    public Query<TYPE> createQuery(
            InstanceHandle handle,
            int maxSamples,
            boolean isReadOrTakeNext);

    /**
     * Create and return a new query to read or take samples of the
     * identified instance (or the "next" instance) and with the given
     * {@link Subscriber.ReaderState}.
     * 
     * The behavior of isReadOrTakeNext is the same as in
     * {@link #createQuery(InstanceHandle, int, boolean)}.
     * 
     * @param handle            The instance to read or take. If this handle
     *                          is null, or if it is nil and isReadOrTakeNext
     *                          is false, the read or take will return samples
     *                          of all available instances.
     * @param maxSamples        The maximum number of samples to read or take
     *                          at one time, or {@link Integer#MAX_VALUE} if
     *                          all available samples should be returned.
     * @param isReadOrTakeNext  If this flag is set to false,
     *                          the read or take will return samples of the
     *                          identified instance. If it is true, the read
     *                          or take will return samples of the "next"
     *                          instance in order. This behavior allows
     *                          applications to easily iterate over the
     *                          available data one instance at a time.
     * @param state             The read or take will only return samples
     *                          match these states.
     * 
     * @return  a new Query.
     * 
     * @throws  IllegalArgumentException    if the handle is null and
     *                                      isReadOrTakeNext is true, or if
     *                                      the handle does not correspond to
     *                                      an existing data object known to
     *                                      this DataReader.
     * 
     * @see     #createQuery(InstanceHandle, int, boolean)
     * @see     #createQuery(InstanceHandle, int, boolean, ReadCondition)
     * @see     #read(Query)
     * @see     #take(Query)
     */
    public Query<TYPE> createQuery(
            InstanceHandle handle,
            int maxSamples,
            boolean isReadOrTakeNext,
            Subscriber.ReaderState state);

    /**
     * Create and return a new query to read or take samples of the
     * identified instance (or the "next" instance), the availability of
     * which would cause the given {@link ReadCondition} or
     * {@link QueryCondition} to trigger.
     * 
     * In case the ReadCondition is a "plain" ReadCondition and not the
     * specialized QueryCondition, this method is equivalent to calling
     * {@link #createQuery(InstanceHandle, int, boolean, org.omg.dds.sub.Subscriber.ReaderState)}
     * and passing a ReaderState consisting of the sample states, view
     * states, and instance states from the corresponding attributes in the
     * read condition. Using this operation, the application can avoid
     * repeating the same parameters specified when creating the
     * ReadCondition.
     * 
     * The behavior of isReadOrTakeNext is the same as in
     * {@link #createQuery(InstanceHandle, int, boolean)}.
     * 
     * @param handle            The instance to read or take. If this handle
     *                          is null, or if it is nil and isReadOrTakeNext
     *                          is false, the read or take will return samples
     *                          of all available instances.
     * @param maxSamples        The maximum number of samples to read or take
     *                          at one time, or {@link Integer#MAX_VALUE} if
     *                          all available samples should be returned.
     * @param isReadOrTakeNext  If this flag is set to false,
     *                          the read or take will return samples of the
     *                          identified instance. If it is true, the read
     *                          or take will return samples of the "next"
     *                          instance in order. This behavior allows
     *                          applications to easily iterate over the
     *                          available data one instance at a time.
     * @param cond              The read or take will return those samples,
     *                          the availability of which would cause this
     *                          condition to trigger. A
     *                          {@link QueryCondition}, to filter data
     *                          samples based on the content, is especially
     *                          useful.
     * 
     * @return  a new Query.
     * 
     * @throws  IllegalArgumentException        if the handle is null and
     *                                          isReadOrTakeNext is true, or
     *                                          if the handle does not
     *                                          correspond to an existing
     *                                          data object known to this
     *                                          DataReader.
     * @throws  PreconditionNotMetException     if the given condition is not
     *                                          attached to this DataReader.
     * 
     * @see     #createQuery(InstanceHandle, int, boolean)
     * @see     #createQuery(InstanceHandle, int, boolean, org.omg.dds.sub.Subscriber.ReaderState)
     * @see     #read(Query)
     * @see     #take(Query)
     */
    public Query<TYPE> createQuery(
            InstanceHandle handle,
            int maxSamples,
            boolean isReadOrTakeNext,
            ReadCondition<TYPE> cond);


    /**
     * A DataReader.Query encapsulates the various ways that an application
     * can search for data to read or take from a {@link DataReader}.
     */
    public static interface Query<T> extends DDSObject, Cloneable {
        // --- Query Access: --- //

        /**
         * If this method returns a non-nil handle, the application intends
         * to read or take only those samples pertaining to the given
         * instance (or the "next" instance). If it returns a nil handle, the
         * application intends to read or take samples pertaining to all
         * available instances.
         * 
         * This method shall never return null.
         */
        public InstanceHandle getInstance();

        /**
         * @return      the maximum number of samples to read or take at one
         *              time, or {@link Integer#MAX_VALUE} if all available
         *              samples should be returned.
         */
        public int getMaxSamples();

        /**
         * If this flag is set to false, the read or take will return samples
         * of the identified instance. If it is true, the read or take will
         * return samples of the "next" instance in order. This behavior
         * allows applications to easily iterate over the available data one
         * instance at a time.
         * 
         * If no particular instance is specified, this method shall always
         * return false.
         */
        public boolean isReadOrTakeNext();

        /**
         * The application intends to read or take only those samples with
         * the given ReaderState.
         * 
         * If this Query was initialized with a {@link ReadCondition} or
         * {@link QueryCondition}, this method shall return the
         * ReaderState associated with that condition.
         * 
         * The result shall be unmodifiable.
         */
        public Subscriber.ReaderState getReaderState();

        /**
         * The application intends only to read or take those samples, the
         * availability of which would cause the given {@link ReadCondition}
         * or {@link QueryCondition} to trigger.
         */
        public ReadCondition<T> getCondition();


        // --- From Object: --------------------------------------------------

        public Query<T> clone();

        public boolean equals(Object other);

        public int hashCode();
    }
}
